// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ssa

// copyelim从f中删除OpCopy值的所有用法。
// 实际删除副本需要后续的死码过程。
func copyelim(f *Func) {
	// 修改所有值，使任何参数（包括OpCopy的参数
	// ）都不是副本。
	for _, b := range f.Blocks {
		for _, v := range b.Values {
			copyelimValue(v)
		}
	}

	// 更新块控制值。
	for _, b := range f.Blocks {
		for i, v := range b.ControlValues() {
			if v.Op == OpCopy {
				b.ReplaceControl(i, v.Args[0])
			}
		}
	}

	// 更新命名值。
	for _, name := range f.Names {
		values := f.NamedValues[*name]
		for i, v := range values {
			if v.Op == OpCopy {
				values[i] = v.Args[0]
			}
		}
	}
}

// copySource返回（非复制）op，即
// v的最终源必须是一个复制op.
func copySource(v *Value) *Value {
	w := v.Args[0]

	// 这个循环只是：
	// 对于w.op==OpCopy{
	// w=w.Args[0]
	// }
	// 但是我们要特别注意确保我们
	// 不要陷入无限循环。
	// 无限复制循环可能发生在无法访问的代码中。
	// /（TODO:或者可以吗？需要测试。）
	slow := w
	var advance bool
	for w.Op == OpCopy {
		w = w.Args[0]
		if w == slow {
			w.reset(OpUnknown)
			break
		}
		if advance {
			slow = slow.Args[0]
		}
		advance = !advance
	}

	// 答案是w。更新我们看到的所有副本
	// 直接指向w。执行此更新将使
	// 确保我们不会为n个副本链执行O（n^2）工作
	// 操作。
	for v != w {
		x := v.Args[0]
		v.SetArg(0, w)
		v = x
	}
	return w
}

// copyelimValue确保v的任何参数都不是副本。
func copyelimValue(v *Value) {
	for i, a := range v.Args {
		if a.Op == OpCopy {
			v.SetArg(i, copySource(a))
		}
	}
}
